home *** CD-ROM | disk | FTP | other *** search
/ Die Speccy' 97 / Die Speccy' 97.iso / amiga_system / the_aminet / dev / lang / python020.lha / python / lib / bdb.py < prev    next >
Text File  |  1995-10-22  |  9KB  |  363 lines

  1. # A generic Python debugger base class.
  2. # This class takes care of details of the trace facility;
  3. # a derived class should implement user interaction.
  4. # There are two debuggers based upon this:
  5. # 'pdb', a text-oriented debugger not unlike dbx or gdb;
  6. # and 'wdb', a window-oriented debugger.
  7. # And of course... you can roll your own!
  8.  
  9. import sys
  10.  
  11. BdbQuit = 'bdb.BdbQuit' # Exception to give up completely
  12.  
  13.  
  14. class Bdb: # Basic Debugger
  15.     
  16.     def __init__(self):
  17.         self.breaks = {}
  18.     
  19.     def reset(self):
  20.         import linecache
  21.         linecache.checkcache()
  22.         self.botframe = None
  23.         self.stopframe = None
  24.         self.returnframe = None
  25.         self.quitting = 0
  26.     
  27.     def trace_dispatch(self, frame, event, arg):
  28.         if self.quitting:
  29.             return # None
  30.         if event == 'line':
  31.             return self.dispatch_line(frame)
  32.         if event == 'call':
  33.             return self.dispatch_call(frame, arg)
  34.         if event == 'return':
  35.             return self.dispatch_return(frame, arg)
  36.         if event == 'exception':
  37.             return self.dispatch_exception(frame, arg)
  38.         print 'bdb.Bdb.dispatch: unknown debugging event:', `event`
  39.         return self.trace_dispatch
  40.     
  41.     def dispatch_line(self, frame):
  42.         if self.stop_here(frame) or self.break_here(frame):
  43.             self.user_line(frame)
  44.             if self.quitting: raise BdbQuit
  45.         return self.trace_dispatch
  46.     
  47.     def dispatch_call(self, frame, arg):
  48.         frame.f_locals['__args__'] = arg
  49.         if self.botframe is None:
  50.             # First call of dispatch since reset()
  51.             self.botframe = frame
  52.             return self.trace_dispatch
  53.         if not (self.stop_here(frame) or self.break_anywhere(frame)):
  54.             # No need to trace this function
  55.             return # None
  56.         self.user_call(frame, arg)
  57.         if self.quitting: raise BdbQuit
  58.         return self.trace_dispatch
  59.     
  60.     def dispatch_return(self, frame, arg):
  61.         if self.stop_here(frame) or frame == self.returnframe:
  62.             self.user_return(frame, arg)
  63.             if self.quitting: raise BdbQuit
  64.     
  65.     def dispatch_exception(self, frame, arg):
  66.         if self.stop_here(frame):
  67.             self.user_exception(frame, arg)
  68.             if self.quitting: raise BdbQuit
  69.         return self.trace_dispatch
  70.     
  71.     # Normally derived classes don't override the following
  72.     # methods, but they may if they want to redefine the
  73.     # definition of stopping and breakpoints.
  74.     
  75.     def stop_here(self, frame):
  76.         if self.stopframe is None:
  77.             return 1
  78.         if frame is self.stopframe:
  79.             return 1
  80.         while frame is not None and frame is not self.stopframe:
  81.             if frame is self.botframe:
  82.                 return 1
  83.             frame = frame.f_back
  84.         return 0
  85.     
  86.     def break_here(self, frame):
  87.         if not self.breaks.has_key(frame.f_code.co_filename):
  88.             return 0
  89.         if not frame.f_lineno in \
  90.                 self.breaks[frame.f_code.co_filename]:
  91.             return 0
  92.         return 1
  93.     
  94.     def break_anywhere(self, frame):
  95.         return self.breaks.has_key(frame.f_code.co_filename)
  96.     
  97.     # Derived classes should override the user_* methods
  98.     # to gain control.
  99.     
  100.     def user_call(self, frame, argument_list):
  101.         # This method is called when there is the remote possibility
  102.         # that we ever need to stop in this function
  103.         pass
  104.     
  105.     def user_line(self, frame):
  106.         # This method is called when we stop or break at this line
  107.         pass
  108.     
  109.     def user_return(self, frame, return_value):
  110.         # This method is called when a return trap is set here
  111.         pass
  112.     
  113.     def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
  114.         # This method is called if an exception occurs,
  115.         # but only if we are to stop at or just below this level
  116.         pass
  117.     
  118.     # Derived classes and clients can call the following methods
  119.     # to affect the stepping state.
  120.     
  121.     def set_step(self):
  122.         # Stop after one line of code
  123.         self.stopframe = None
  124.         self.returnframe = None
  125.         self.quitting = 0
  126.     
  127.     def set_next(self, frame):
  128.         # Stop on the next line in or below the given frame
  129.         self.stopframe = frame
  130.         self.returnframe = None
  131.         self.quitting = 0
  132.     
  133.     def set_return(self, frame):
  134.         # Stop when returning from the given frame
  135.         self.stopframe = frame.f_back
  136.         self.returnframe = frame
  137.         self.quitting = 0
  138.     
  139.     def set_trace(self):
  140.         # Start debugging from here
  141.         try:
  142.             1 + ''
  143.         except:
  144.             frame = sys.exc_traceback.tb_frame.f_back
  145.         self.reset()
  146.         while frame:
  147.             frame.f_trace = self.trace_dispatch
  148.             self.botframe = frame
  149.             frame = frame.f_back
  150.         self.set_step()
  151.         sys.settrace(self.trace_dispatch)
  152.  
  153.     def set_continue(self):
  154.         # Don't stop except at breakpoints or when finished
  155.         self.stopframe = self.botframe
  156.         self.returnframe = None
  157.         self.quitting = 0
  158.         if not self.breaks:
  159.             # no breakpoints; run without debugger overhead
  160.             sys.settrace(None)
  161.             try:
  162.                 1 + ''    # raise an exception
  163.             except:
  164.                 frame = sys.exc_traceback.tb_frame.f_back
  165.             while frame and frame is not self.botframe:
  166.                 del frame.f_trace
  167.                 frame = frame.f_back
  168.     
  169.     def set_quit(self):
  170.         self.stopframe = self.botframe
  171.         self.returnframe = None
  172.         self.quitting = 1
  173.         sys.settrace(None)
  174.     
  175.     # Derived classes and clients can call the following methods
  176.     # to manipulate breakpoints.  These methods return an
  177.     # error message is something went wrong, None if all is well.
  178.     # Call self.get_*break*() to see the breakpoints.
  179.     
  180.     def set_break(self, filename, lineno):
  181.         import linecache # Import as late as possible
  182.         line = linecache.getline(filename, lineno)
  183.         if not line:
  184.             return 'That line does not exist!'
  185.         if not self.breaks.has_key(filename):
  186.             self.breaks[filename] = []
  187.         list = self.breaks[filename]
  188.         if lineno in list:
  189.             return 'There is already a breakpoint there!'
  190.         list.append(lineno)
  191.     
  192.     def clear_break(self, filename, lineno):
  193.         if not self.breaks.has_key(filename):
  194.             return 'There are no breakpoints in that file!'
  195.         if lineno not in self.breaks[filename]:
  196.             return 'There is no breakpoint there!'
  197.         self.breaks[filename].remove(lineno)
  198.         if not self.breaks[filename]:
  199.             del self.breaks[filename]
  200.     
  201.     def clear_all_file_breaks(self, filename):
  202.         if not self.breaks.has_key(filename):
  203.             return 'There are no breakpoints in that file!'
  204.         del self.breaks[filename]
  205.     
  206.     def clear_all_breaks(self):
  207.         if not self.breaks:
  208.             return 'There are no breakpoints!'
  209.         self.breaks = {}
  210.     
  211.     def get_break(self, filename, lineno):
  212.         return self.breaks.has_key(filename) and \
  213.             lineno in self.breaks[filename]
  214.     
  215.     def get_file_breaks(self, filename):
  216.         if self.breaks.has_key(filename):
  217.             return self.breaks[filename]
  218.         else:
  219.             return []
  220.     
  221.     def get_all_breaks(self):
  222.         return self.breaks
  223.     
  224.     # Derived classes and clients can call the following method
  225.     # to get a data structure representing a stack trace.
  226.     
  227.     def get_stack(self, f, t):
  228.         stack = []
  229.         if t and t.tb_frame is f:
  230.             t = t.tb_next
  231.         while f is not None:
  232.             stack.append((f, f.f_lineno))
  233.             if f is self.botframe:
  234.                 break
  235.             f = f.f_back
  236.         stack.reverse()
  237.         i = max(0, len(stack) - 1)
  238.         while t is not None:
  239.             stack.append((t.tb_frame, t.tb_lineno))
  240.             t = t.tb_next
  241.         return stack, i
  242.     
  243.     # 
  244.     
  245.     def format_stack_entry(self, frame_lineno, lprefix=': '):
  246.         import linecache, repr, string
  247.         frame, lineno = frame_lineno
  248.         filename = frame.f_code.co_filename
  249.         s = filename + '(' + `lineno` + ')'
  250.         if frame.f_code.co_name:
  251.             s = s + frame.f_code.co_name
  252.         else:
  253.             s = s + "<lambda>"
  254.         if frame.f_locals.has_key('__args__'):
  255.             args = frame.f_locals['__args__']
  256.         else:
  257.             args = None
  258.         if args:
  259.             s = s + repr.repr(args)
  260.         else:
  261.             s = s + '()'
  262.         if frame.f_locals.has_key('__return__'):
  263.             rv = frame.f_locals['__return__']
  264.             s = s + '->'
  265.             s = s + repr.repr(rv)
  266.         line = linecache.getline(filename, lineno)
  267.         if line: s = s + lprefix + string.strip(line)
  268.         return s
  269.     
  270.     # The following two methods can be called by clients to use
  271.     # a debugger to debug a statement, given as a string.
  272.     
  273.     def run(self, cmd, globals=None, locals=None):
  274.         if globals is None:
  275.             import __main__
  276.             globals = __main__.__dict__
  277.         if locals is None:
  278.             locals = globals
  279.         self.reset()
  280.         sys.settrace(self.trace_dispatch)
  281.         try:
  282.             try:
  283.                 exec cmd + '\n' in globals, locals
  284.             except BdbQuit:
  285.                 pass
  286.         finally:
  287.             self.quitting = 1
  288.             sys.settrace(None)
  289.     
  290.     def runeval(self, expr, globals=None, locals=None):
  291.         if globals is None:
  292.             import __main__
  293.             globals = __main__.__dict__
  294.         if locals is None:
  295.             locals = globals
  296.         self.reset()
  297.         sys.settrace(self.trace_dispatch)
  298.         try:
  299.             try:
  300.                 return eval(expr + '\n', globals, locals)
  301.             except BdbQuit:
  302.                 pass
  303.         finally:
  304.             self.quitting = 1
  305.             sys.settrace(None)
  306.  
  307.     def runctx(self, cmd, globals, locals):
  308.         # B/W compatibility
  309.         self.run(cmd, globals, locals)
  310.  
  311.     # This method is more useful to debug a single function call.
  312.  
  313.     def runcall(self, func, *args):
  314.         self.reset()
  315.         sys.settrace(self.trace_dispatch)
  316.         res = None
  317.         try:
  318.             try:
  319.                 res = apply(func, args)
  320.             except BdbQuit:
  321.                 pass
  322.         finally:
  323.             self.quitting = 1
  324.             sys.settrace(None)
  325.         return res
  326.  
  327.  
  328. def set_trace():
  329.     Bdb().set_trace()
  330.  
  331. # -------------------- testing --------------------
  332.  
  333. class Tdb(Bdb):
  334.     def user_call(self, frame, args):
  335.         name = frame.f_code.co_name
  336.         if not name: name = '???'
  337.         print '+++ call', name, args
  338.     def user_line(self, frame):
  339.         import linecache, string
  340.         name = frame.f_code.co_name
  341.         if not name: name = '???'
  342.         fn = frame.f_code.co_filename
  343.         line = linecache.getline(fn, frame.f_lineno)
  344.         print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
  345.     def user_return(self, frame, retval):
  346.         print '+++ return', retval
  347.     def user_exception(self, frame, exc_stuff):
  348.         print '+++ exception', exc_stuff
  349.         self.set_continue()
  350.  
  351. def foo(n):
  352.     print 'foo(', n, ')'
  353.     x = bar(n*10)
  354.     print 'bar returned', x
  355.  
  356. def bar(a):
  357.     print 'bar(', a, ')'
  358.     return a/2
  359.  
  360. def test():
  361.     t = Tdb()
  362.     t.run('import bdb; bdb.foo(10)')
  363.